/*
	* Created by : Varun Tyagi.
	*
	* This tutorial is a simple one. All it does it use joystick instead of keyboard. 
*/

/* Header files. */
#include "SDL.h"
#include "SDL_image.h"
#include <string>

/* Screen attributes. */
const int SCREEN_HEIGHT	= 640;
const int SCREEN_WIDTH	= 480;
const int SCREEN_BPP	= 32;

/* Frame Rate. */
const int FRAMES_PER_SECOND = 20;

/* Dot Dimension. */
const int DOT_WIDTH		= 20;
const int DOT_HEIGHT	= 20;

/* The surfaces. */
SDL_Surface* pScreen = nullptr;
SDL_Surface* pDot = nullptr;

/* The event structure. */
SDL_Event event;

/* Joystick. */
SDL_Joystick* pStick = nullptr;

/* Dot. */
class Dot
{
private:

	/* Offset of the dot. */
	int m_iX, m_iY;

	/* Velocity of the dot. */
	int m_iVelX, m_iVelY;

public:

	/* Initialize. */
	Dot();

	/* Input Handler. */
	void handl_input( void );

	/* Move the dot. */
	void move_dot( void );

	/* Shoe the dot. */
	void show_dot( void );
};

/* Timer. */
class Timer
{
private:

	/* The start tick of the timer. */
	int m_iStartTime;

	/* The pause tick, when the timer was paused. */
	int m_iPauseTime;

	/* Status of the timer. */
	bool m_bStart;
	bool m_bPause;

public:

	/* Initialize. */
	Timer();

	/* Timer functions. */
	void start( void );
	void stop( void );
	void pause( void );
	void unpause( void );

	/* Get the timer tick. */
	int get_ticks( void );

	/* Get the status of the timer. */
	bool has_started( void );
	bool is_paused( void );
};

/* Image loader. */
SDL_Surface *load_image( std::string filename )
{
	/* The image that has been loaded. */
	SDL_Surface *pLoadedImage = nullptr;

	/* The optimized image. */
	SDL_Surface *pOptimizedImage = nullptr;

	/* Load the image. */
	pLoadedImage = IMG_Load( filename.c_str() );

	/* Check the loaded image. */
	if( nullptr != pLoadedImage )
	{
		/* Optimize the image. */
		pOptimizedImage = SDL_DisplayFormat( pLoadedImage );

		/* Check the optimized image. */
		if( nullptr != pOptimizedImage )
		{
			/* Color key surface. */
			SDL_SetColorKey( pOptimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( pOptimizedImage->format, 0xFF, 0xFF, 0xFF ) );
		}
	}

	/* Return the optimized image. */
	return pOptimizedImage;
}

/* Surface applier. */
void apply_surface( int x, int y, SDL_Surface* pSource, SDL_Surface* pDestination, SDL_Rect* pRects = nullptr )
{
	/* The offset. */
	SDL_Rect offset;
	
	/* Get the offsets. */
	offset.x = x;
	offset.y = y;

	/* Blit the surface. */
	SDL_BlitSurface( pSource, pRects, pDestination, &offset );
}

/*Initialize. */
bool init( void )
{
	/* Init SDL. */
	if( -1 == SDL_Init( SDL_INIT_EVERYTHING ) )
	{
		return false;
	}

	/* Set up the screen. */
	pScreen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

	/* Check the screen. */
	if( nullptr == pScreen )
	{
		return false;
	}

	/* Check to see if there is any joystick present. */
	if( 1 > SDL_NumJoysticks() )
	{
		return false;
	}

	/* Open the joystick. */
	pStick = SDL_JoystickOpen( 0 );

	/* Check the loaded joystick. */
	if( nullptr == pStick )
	{
		return false;
	}

	/* Set the window caption. */
	SDL_WM_SetCaption( "Joystick handler", nullptr );

	/* If everything went fine, return true. */
	return true;
}

/* Load files. */
bool load_file( void )
{
	/* Load the background image. */
	pDot = load_image( "dot.bmp" );

	/* Check the dot. */
	if( nullptr == pDot )
	{
		return false;
	}

	/* Else everything went fine, return true. */
	return true;
}

/* Clean up. */
void clean_SDL( void ) 
{
	/* Free the surface. */
	SDL_FreeSurface( pDot );

	/* Close the joystick. */
	SDL_JoystickClose( pStick );

	/* QUIT. */
	SDL_Quit();
}

/************ DOT *************/
Dot::Dot()
{
	/* Set the offsets. */
	m_iX = m_iY = 0;

	/* Set the velocity. */
	m_iVelX = m_iVelY = 0;
}

/* Input handler. */
void Dot::handl_input( void )
{
	/* If a axis was changed. */
	if( SDL_JOYAXISMOTION == event.type )
	{
		/* If joystick 0 has moved. */
		if( 0 == event.jaxis.which )
		{
			/* If the X axis has changed. */
			if( 0 == event.jaxis.axis )
			{
				/* If X axis is neutral. */
				if( ( -8000 < event.jaxis.value ) && ( 8000 > event.jaxis.value ) )
				{
					m_iVelX = 0;
				}
				else
				{
					/* Adjust the velocity. */
					if( 0 > event.jaxis.value )
					{
						m_iVelX = -DOT_WIDTH / 2;
					}
					else
					{
						m_iVelX = DOT_WIDTH / 2;
					}
				}
			}

			/* If Y axis has changed. */
			else if( 1 == event.jaxis.axis )
			{
				/* Check if Y axis is neutral. */
				if( ( -8000 < event.jaxis.value ) && ( 8000 > event.jaxis.value ) )
				{
					m_iVelY = 0;
				}
				else
				{
					/* Adjust the velocity. */
					if( 0 > event.jaxis.value )
					{
						m_iVelY = -DOT_HEIGHT / 2;
					}
					else
					{
						m_iVelY = DOT_HEIGHT / 2;
					}
				}
			}
		}
	}
}

/* Move the dot. */
void Dot::move_dot( void )
{
	/* Move the dot left or right. */
	m_iX += m_iVelX;

	/* Check if the dot went too far that it is going off screen. */
	if( ( 0 > m_iX ) || ( m_iX + DOT_WIDTH > SCREEN_WIDTH ) )
	{
		/* Reverse the dot. */
		m_iX -= m_iVelY;
	}

	/* Move the dot up or down. */
	m_iY += m_iVelY;

	/* Check to see the dot went too far that it is going off screen. */
	if( ( 0 > m_iY ) || ( m_iY + DOT_HEIGHT > SCREEN_HEIGHT ) )
	{
		/* Reverse the dot. */
		m_iY -= m_iVelY;
	}
}

/* Show the dot. */
void Dot::show_dot( void )
{
	apply_surface( m_iX, m_iY, pDot, pScreen );
}

/************ TIMER *************/
Timer::Timer()
{
	/* Initialize. */
	m_iStartTime = m_iPauseTime = 0;

	/* Set the status. */
	m_bPause = m_bStart = 0;
}

void Timer::start( void )
{
	/* Start the timer. */
	m_bStart = true;

	/* Unpause. */
	m_bPause = false;

	/* Get the starting tick. */
	m_iStartTime = SDL_GetTicks();
}

void Timer::stop( void )
{
	/* Stop the timer. */
	m_bStart = false;

	/* Unpause the timer. */
	m_bPause = false;
}

void Timer::pause( void )
{
	/* if the timer is already running and not paused. */
	if( ( true == m_bStart ) && ( false == m_bPause ) )
	{
		/* Pause the timer. */
		m_bPause = true;

		/* Get the tick when the timer was paused. */
		m_iPauseTime = SDL_GetTicks() - m_iStartTime;
	}
}

void Timer::unpause( void )
{
	/* Check if the timer is paused. */
	if( true == m_bPause )
	{
		/* Unpause. */
		m_bPause = false;

		/* Reset the starting tick. */
		m_iStartTime = SDL_GetTicks() - m_iPauseTime;

		/* Reset the pause timer. */
		m_iPauseTime = 0;
	}
}

int Timer::get_ticks( void )
{
	/* If the timer is running. */
	if( true == m_bStart )
	{
		/* If the timer is paused. */
		if( true == m_bPause )
		{
			/* Return the tick when the timer was paused. */
			return m_iPauseTime;
		}
		else
		{
			/* Return the current time minus the start time. */
			return SDL_GetTicks() - m_iStartTime;
		}
	}

	/* If not running return 0. */
	return 0;
}

bool Timer::has_started( void )
{
	return m_bStart;
}

bool Timer::is_paused( void )
{
	return m_bPause;
}


/* MAIN. */
int main( int argc, char** argv )
{
	/* QUIT flag. */
	bool bQuit = false;

	/* Make the dot. */
	Dot myDot;

	/* Frame regulator. */
	Timer fps;

	/* Initialize. */
	if( false == init() )
	{
		return 1;
	}

	/* Load the files. */
	if( false == load_file() )
	{
		return 1;
	}

	/* WHile the user hasn't quit. */
	while( false == bQuit )
	{
		/* Start the frame queue. */
		fps.start();

		/* while there is event to handle. */
		while( SDL_PollEvent( &event ) )
		{
			/* Handle the inputs. */
			myDot.handl_input();

			/* If the user has Xed out the window. */
			if( SDL_QUIT == event.type )
			{
				/* QUit. */
				bQuit = true;
			}
		}

		/* Move the dot. */
		myDot.move_dot();

		/* Fill the screen white. */
		SDL_FillRect( pScreen, &pScreen->clip_rect, SDL_MapRGB( pScreen->format, 0xFF, 0xFF, 0xFf ) );

		/* Show the dot on the screen. */
		myDot.show_dot();

		/* Update the screen. */
		if( SDL_Flip( pScreen ) == 1 )
		{
			return 1;
		}

		/* Cap the frames. */
		if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
		{
			SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
		}
	}

	/* Clean SDL. */
	clean_SDL();

	return 0;
}



